
#include "RtBase.h"
#include "RtReactorWin32Message.h"

//////////////////////////////////////////////////////////////////////
// class CRtReactorWin32Message
//////////////////////////////////////////////////////////////////////

#define WM_WIN32_SOCKET_SELECT    WM_USER+33
#define WM_WIN32_SOCKET_NOTIFY    WM_USER+34
#define WIN32_SOCKET_CLASS_NAME	  "RtWin32SocketNotification"
#define RT_WINDOW_TIMER_ID 123

HINSTANCE g_pReactorWin3Instance;
ATOM g_atomRegisterClass;

LRESULT CALLBACK CRtReactorWin32Message::
Win32SocketWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{	
	switch(uMsg)
	{
	case WM_WIN32_SOCKET_SELECT: 
	{
		CRtReactorWin32Message *pReactor = (CRtReactorWin32Message *)::GetWindowLong(hwnd, 0);
		IRtEventHandler::MASK maskEvent = IRtEventHandler::NULL_MASK;
		RT_HANDLE sockHandle = (RT_HANDLE)wParam;
		RT_ASSERTE(pReactor);
		RT_ASSERTE(sockHandle != RT_INVALID_HANDLE);

		RtResult rvError = RT_OK;
		int nErrorCode = WSAGETSELECTERROR(lParam);
		if (nErrorCode != 0)
			rvError = RT_ERROR_NETWORK_SOCKET_ERROR;

		BOOL bHandle = TRUE;
		switch (WSAGETSELECTEVENT(lParam))
		{
		case FD_ACCEPT:
			maskEvent |= IRtEventHandler::ACCEPT_MASK;
			break;
		case FD_READ:
			maskEvent |= IRtEventHandler::READ_MASK;
			break;
		case FD_WRITE:
			maskEvent |= IRtEventHandler::WRITE_MASK;
			break;
		case FD_CONNECT:
			maskEvent |= IRtEventHandler::CONNECT_MASK;
			break;
		case FD_CLOSE:
		//	RT_ASSERTE(nErrorCode);
			rvError = RT_ERROR_NETWORK_SOCKET_CLOSE;
			break;
		default:
			bHandle = FALSE;
			break;
		}

		if (WSAGETSELECTEVENT(lParam) == FD_CLOSE && nErrorCode == 0) {
			// this is a graceful close, 
			// we must check the remain data in the socket.
// 			for ( ; ; ) {
				unsigned long dwRemain = 0;
				int nRet = ::ioctlsocket(
					(RT_SOCKET)sockHandle, 
					FIONREAD, 
					&dwRemain);
				if (nRet == 0 && dwRemain > 0) {
					RT_WARNING_TRACE("CRtReactorWin32Message::Win32SocketWndProc,"
						" data remained in the handle when closing, recv it."
						" dwRemain=" << dwRemain);
					pReactor->ProcessHandleEvent(
						sockHandle, 
						IRtEventHandler::READ_MASK, 
						RT_OK, 
						FALSE);
				}
// 				else
// 					break;
// 			}
		}
		
		if (nErrorCode != 0 || WSAGETSELECTEVENT(lParam) == FD_CLOSE) {
			RT_INFO_TRACE("CRtReactorWin32Message::Win32SocketWndProc, handle is closed."
				" fd=" << sockHandle <<
				" mask=" << maskEvent <<
				" nErrorCode=" << nErrorCode <<
				" lParam=" << lParam <<
				" rvError=" << rvError);
			RT_SET_BITS(maskEvent, IRtEventHandler::CLOSE_MASK);
		}

		if (bHandle)
			pReactor->ProcessHandleEvent(sockHandle, maskEvent, rvError, FALSE);
		else {
			RT_WARNING_TRACE("CRtReactorWin32Message::Win32SocketWndProc, unknown SELECTEVENT"
				" wParam=" << wParam << " lParam=" << lParam);
		}
		
#ifndef RT_ENABLE_CALENDAR_TIMER
		pReactor->ProcessTimerTick();
#endif // !RT_ENABLE_CALENDAR_TIMER
		return 0;
	}

	case WM_WIN32_SOCKET_NOTIFY:
	{
		CRtReactorWin32Message *pReactor = (CRtReactorWin32Message *)::GetWindowLong(hwnd, 0);
		RT_HANDLE fdOn = (RT_HANDLE)wParam;
		IRtEventHandler::MASK maskEh = (IRtEventHandler::MASK)lParam;
		RT_ASSERTE(pReactor);

		pReactor->ProcessHandleEvent(fdOn, maskEh, RT_OK, TRUE);
#ifndef RT_ENABLE_CALENDAR_TIMER
		pReactor->ProcessTimerTick();
#endif // !RT_ENABLE_CALENDAR_TIMER
		return 0;
	}

	case WM_TIMER:
	{
		CRtReactorWin32Message *pReactor = (CRtReactorWin32Message *)::GetWindowLong(hwnd, 0);
		RT_ASSERTE(pReactor);

#ifdef RT_ENABLE_CALENDAR_TIMER
		pReactor->m_CalendarTimer.TimerTick();
#else
		pReactor->ProcessTimerTick();
#endif // RT_ENABLE_CALENDAR_TIMER
		
		break;
	}
	
	case WM_DESTROY: 
		// Post the WM_QUIT message to quit. 
		RT_INFO_TRACE("CRtReactorWin32Message::Win32SocketWndProc, WM_DESTROY");
		::PostQuitMessage(0); 
		return 0; 

	default :
		break;
	}

	return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}

CRtReactorWin32Message::CRtReactorWin32Message()
	: m_hwndNotify(NULL)
	, m_dwTimerId(0)
#ifdef RT_ENABLE_CALENDAR_TIMER
	, m_CalendarTimer(50, 1000*60*60*2, static_cast<CRtEventQueueUsingMutex*>(this))
#endif // RT_ENABLE_CALENDAR_TIMER
{
}

CRtReactorWin32Message::~CRtReactorWin32Message()
{
	RT_INFO_TRACE_THIS("CRtReactorWin32Message::~CRtReactorWin32Message m_hwndNotify="<<m_hwndNotify);
	Close();
}

RtResult CRtReactorWin32Message::Open()
{
	RtResult rv = RT_ERROR_UNEXPECTED;
	g_pReactorWin3Instance = ::GetModuleHandle(NULL);
	RT_ASSERTE_RETURN(!m_hwndNotify, RT_ERROR_ALREADY_INITIALIZED);

#ifdef RT_ENABLE_CALENDAR_TIMER
	m_CalendarTimer.m_Est.Reset2CurrentThreadId();
#endif // RT_ENABLE_CALENDAR_TIMER

	rv = CRtReactorBase::Open();
	if (RT_FAILED(rv))
		goto fail;

	if (g_atomRegisterClass == 0) {
		WNDCLASS wc;
		wc.style = 0;
		wc.lpfnWndProc = Win32SocketWndProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = sizeof(void*);
		wc.hInstance = g_pReactorWin3Instance;
		wc.hIcon = 0;
		wc.hCursor = 0;
		wc.hbrBackground = 0;
		wc.lpszMenuName = NULL;
		wc.lpszClassName = WIN32_SOCKET_CLASS_NAME;
		
		if ((g_atomRegisterClass = ::RegisterClass(&wc)) == 0) {
			RT_ERROR_TRACE_THIS("CRtReactorWin32Message::Open, RegisterClass() failed!"
				" err=" << ::GetLastError());
			rv = RT_ERROR_FAILURE;
			goto fail;
		}
	}

	m_hwndNotify = ::CreateWindow(WIN32_SOCKET_CLASS_NAME, NULL, WS_OVERLAPPED, 0, 
		0, 0, 0, NULL, NULL, g_pReactorWin3Instance, 0);
	if (!m_hwndNotify) {
		RT_ERROR_TRACE_THIS("CRtReactorWin32Message::Open, CreateWindow() failed!"
			" err=" << ::GetLastError());
		rv = RT_ERROR_FAILURE;
		goto fail;
	}

	::SetLastError(0);
	if (::SetWindowLong(m_hwndNotify, 0, (LONG)this) == 0  && ::GetLastError() != 0) {
		RT_ERROR_TRACE_THIS("CRtReactorWin32Message::Open, SetWindowLong() failed!"
			" err=" << ::GetLastError());
		rv = RT_ERROR_FAILURE;
		goto fail;
	}

	m_dwTimerId = ::SetTimer(m_hwndNotify, RT_WINDOW_TIMER_ID, g_dwDefaultTimerTickInterval, NULL);
	if (m_dwTimerId == 0) {
		RT_ERROR_TRACE_THIS("CRtReactorWin32Message::Open, SetTimer() failed!"
			" err=" << ::GetLastError());
		rv = RT_ERROR_FAILURE;
		goto fail;
	}

	CRtStopFlag::m_bStoppedFlag = FALSE;
	RT_INFO_TRACE_THIS("CRtReactorWin32Message::Open, m_hwndNotify="<<m_hwndNotify);
	return RT_OK;

fail:
	Close();
	RT_ASSERTE(RT_FAILED(rv));
	return rv;
}

RtResult CRtReactorWin32Message::
NotifyHandler(IRtEventHandler *aEh, IRtEventHandler::MASK aMask)
{
	// Different threads may call this function due to EventQueue.
	RT_HANDLE fdNew = RT_INVALID_HANDLE;
	if (aEh) {
		m_Est.EnsureSingleThread();
		fdNew = aEh->GetHandle();
		RT_ASSERTE(fdNew != RT_INVALID_HANDLE);
	}
	
	BOOL bRet = ::PostMessage(m_hwndNotify, WM_WIN32_SOCKET_NOTIFY, 
							  (WPARAM)fdNew, (LPARAM)aMask);
	if (!bRet) {
		RT_ERROR_TRACE_THIS("CRtReactorWin32Message::NotifyHandler, PostMessage() failed!"
			" err=" << ::GetLastError()<<" m_hwndNotify="<<m_hwndNotify);
		return RT_ERROR_UNEXPECTED;
	}
	else
		return RT_OK;
}

RtResult CRtReactorWin32Message::RunEventLoop()
{
	RT_INFO_TRACE_THIS("CRtReactorWin32Message::RunEventLoop");
	m_Est.EnsureSingleThread();

	MSG msg;
	while (!CRtStopFlag::m_bStoppedFlag && ::GetMessage(&msg, NULL, 0, 0)) {
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}
	return RT_OK;
}

// this function can be invoked in the different thread.
RtResult CRtReactorWin32Message::StopEventLoop()
{
	RT_INFO_TRACE_THIS("CRtReactorWin32Message::StopEventLoop");

//	::PostQuitMessage(0);
	CRtStopFlag::m_bStoppedFlag = TRUE;
	
	CRtEventQueueUsingMutex::Stop();
	return RT_OK;
}

RtResult CRtReactorWin32Message::Close()
{
	RT_INFO_TRACE_THIS("CRtReactorWin32Message::Close, m_hwndNotify="<<m_hwndNotify);
	if (m_hwndNotify) {
		BOOL rv = FALSE;
		if (m_dwTimerId>0)
		{
			rv = ::KillTimer(m_hwndNotify, m_dwTimerId);
			RT_INFO_TRACE_THIS("CRtReactorWin32Message::Close, KillTimer rv="<<rv<<" err="<<::GetLastError());
			m_dwTimerId = 0;
		}
		rv = ::DestroyWindow(m_hwndNotify);
		RT_INFO_TRACE_THIS("CRtReactorWin32Message::Close, DestroyWindow rv="<<rv<<" err="<<::GetLastError());
		m_hwndNotify = NULL;
	}
	return CRtReactorBase::Close();
}

RtResult CRtReactorWin32Message::
OnHandleRegister(RT_HANDLE aFd, IRtEventHandler::MASK aMask, IRtEventHandler *aEh)
{
	m_Est.EnsureSingleThread();
	RT_ERROR_TRACE_THIS("CRtReactorWin32Message::OnHandleRegister,"
		" aFd=" << aFd <<
		" aMask=" << aMask);
	return RT_ERROR_UNEXPECTED;
}

void CRtReactorWin32Message::OnHandleRemoved(RT_HANDLE aFd)
{
	m_Est.EnsureSingleThread();
	RT_ERROR_TRACE_THIS("CRtReactorWin32Message::OnHandleRemoved, aFd=" << aFd);
}

#ifdef RT_ENABLE_CALENDAR_TIMER
RtResult CRtReactorWin32Message::
ScheduleTimer(IRtTimerHandler *aTh, LPVOID aArg, 
			  const CRtTimeValue &aInterval, DWORD aCount)
{
	return m_CalendarTimer.ScheduleTimer(aTh, aArg, aInterval, aCount);
}

RtResult CRtReactorWin32Message::CancelTimer(IRtTimerHandler *aTh)
{
	return m_CalendarTimer.CancelTimer(aTh);
}
#endif // RT_ENABLE_CALENDAR_TIMER


//////////////////////////////////////////////////////////////////////
// class CRtReactorWin32AsyncSelect
//////////////////////////////////////////////////////////////////////

CRtReactorWin32AsyncSelect::CRtReactorWin32AsyncSelect()
{
}

CRtReactorWin32AsyncSelect::~CRtReactorWin32AsyncSelect()
{
}

RtResult CRtReactorWin32AsyncSelect::
OnHandleRegister(RT_HANDLE aFd, IRtEventHandler::MASK aMask, IRtEventHandler *aEh)
{
	return DoAsyncSelect_i(aFd, IRtEventHandler::ALL_EVENTS_MASK);
}

void CRtReactorWin32AsyncSelect::OnHandleRemoved(RT_HANDLE aFd)
{
	DoAsyncSelect_i(aFd, IRtEventHandler::NULL_MASK);
}

RtResult CRtReactorWin32AsyncSelect::
DoAsyncSelect_i(RT_HANDLE aFd, IRtEventHandler::MASK aMask)
{
	long lEvent = 0;
	if (aMask & IRtEventHandler::CONNECT_MASK)
		lEvent |= FD_CONNECT;
	if (aMask & IRtEventHandler::ACCEPT_MASK)
		lEvent |= FD_ACCEPT;
	if (aMask & IRtEventHandler::READ_MASK)
		lEvent |= FD_READ;
	if (aMask & IRtEventHandler::WRITE_MASK)
		lEvent |= FD_WRITE;

	if (lEvent != 0)
		lEvent |= FD_CLOSE;
	if (::WSAAsyncSelect((SOCKET)aFd, m_hwndNotify, WM_WIN32_SOCKET_SELECT, lEvent) != 0) {
		RT_ERROR_TRACE_THIS("CRtReactorWin32AsyncSelect::DoAsyncSelect_i, WSAAsyncSelect() failed!"
			" aFd=" << aFd <<
			" err=" << ::WSAGetLastError());
		return RT_ERROR_UNEXPECTED;
	}
	else
		return RT_OK;
}
